#!/usr/bin/env python3
"""
生成案例4的14张SHAP分析示例图片
作者：张立强
日期：2025-11-03
"""

import numpy as np
import matplotlib.pyplot as plt
import matplotlib
import os

# 设置中文字体
matplotlib.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'DejaVu Sans']
matplotlib.rcParams['axes.unicode_minus'] = False

# 创建输出目录
os.makedirs('output/cases/figures', exist_ok=True)

# 设置随机种子
np.random.seed(12345)

# 生成模拟数据
n_players = 1108
positions = np.random.choice(['Forward', 'Midfielder', 'Defender', 'Goalkeeper'], n_players)
ages = np.random.randint(18, 36, n_players)
team_win_ratio = 0.2 + np.random.rand(n_players) * 0.6
goals_per_minute = np.random.rand(n_players) * 0.02

# 调整进球效率（前锋更高）
goals_per_minute[positions == 'Forward'] *= 2
goals_per_minute[positions == 'Defender'] *= 0.3
goals_per_minute[positions == 'Goalkeeper'] *= 0.1

# 生成市场价值
base_value = 10 + team_win_ratio * 50 + goals_per_minute * 1000
age_effect = -0.5 * (ages - 26)**2 + 20
market_value = np.exp(np.log(base_value + age_effect) + np.random.randn(n_players) * 0.3)

# 生成SHAP值
shap_team_win_ratio = (team_win_ratio - 0.5) * 2 + np.random.randn(n_players) * 0.2
shap_age = -0.1 * (ages - 26)**2 / 10 + np.random.randn(n_players) * 0.1
shap_goals = goals_per_minute * 50 + np.random.randn(n_players) * 0.1

print("开始生成14张SHAP分析图片...")

# 图1：市场价值分布（按位置）
plt.figure(figsize=(12, 8))
position_order = ['Forward', 'Midfielder', 'Defender', 'Goalkeeper']
data_by_position = [market_value[positions == p] for p in position_order]
bp = plt.boxplot(data_by_position, labels=position_order, patch_artist=True)
for patch in bp['boxes']:
    patch.set_facecolor('lightblue')
plt.title('FIFA Player Market Value by Position\nDistribution across 1,108 players', fontsize=14, fontweight='bold')
plt.ylabel('Market Value (Million EUR)', fontsize=12)
plt.xlabel('Position', fontsize=12)
plt.grid(axis='y', alpha=0.3)
plt.tight_layout()
plt.savefig('output/cases/figures/case04_01_value_by_position.png', dpi=150, bbox_inches='tight')
plt.close()
print("✓ 图1生成完成: case04_01_value_by_position.png")

# 图2：对数价值分布
plt.figure(figsize=(12, 8))
log_values = np.log(market_value)
plt.hist(log_values, bins=50, density=True, alpha=0.7, color='steelblue', edgecolor='black')
# 添加正态分布拟合
mu, sigma = log_values.mean(), log_values.std()
x = np.linspace(log_values.min(), log_values.max(), 100)
plt.plot(x, 1/(sigma * np.sqrt(2 * np.pi)) * np.exp(-0.5 * ((x - mu) / sigma)**2), 
         'r-', linewidth=2, label='Normal fit')
plt.title('Distribution of Log Market Value\nAfter log transformation to handle right skewness', 
          fontsize=14, fontweight='bold')
plt.xlabel('Log(Market Value)', fontsize=12)
plt.ylabel('Density', fontsize=12)
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('output/cases/figures/case04_02_log_value_distribution.png', dpi=150, bbox_inches='tight')
plt.close()
print("✓ 图2生成完成: case04_02_log_value_distribution.png")

# 图3：变量重要性
plt.figure(figsize=(12, 8))
features = ['team_win_ratio', 'age', 'goals_per_minute', 'assist_per_minute', 
            'average_minutes_played', 'height', 'league_level', 'nationality_rank',
            'total_yellow_cards', 'total_red_cards']
importance = [1.000, 0.856, 0.723, 0.645, 0.512, 0.398, 0.367, 0.289, 0.156, 0.089]
y_pos = np.arange(len(features))
plt.barh(y_pos, importance, color='navy', alpha=0.8)
plt.yticks(y_pos, features)
plt.xlabel('Relative Importance', fontsize=12)
plt.title('Variable Importance - GBM Model\nRelative importance in predicting player market value', 
          fontsize=14, fontweight='bold')
plt.grid(axis='x', alpha=0.3)
plt.tight_layout()
plt.savefig('output/cases/figures/case04_03_variable_importance.png', dpi=150, bbox_inches='tight')
plt.close()
print("✓ 图3生成完成: case04_03_variable_importance.png")

# 图4：顶级前锋SHAP瀑布图
plt.figure(figsize=(12, 8))
features_w = ['team_win_ratio', 'goals_per_minute', 'age', 'assist_per_minute', 
              'average_minutes_played', 'league_level', 'nationality_rank', 'height', 'total_yellow_cards']
shap_values_w = [0.85, 0.72, 0.45, 0.23, 0.18, 0.15, 0.12, -0.08, -0.05]
colors = ['green' if v > 0 else 'red' for v in shap_values_w]
y_pos = np.arange(len(features_w))
plt.barh(y_pos, shap_values_w, color=colors, alpha=0.7)
plt.yticks(y_pos, features_w)
plt.xlabel('SHAP Value (Log Scale)', fontsize=12)
plt.title('SHAP Waterfall Plot - Top Forward\nIndividual Feature Contributions to Market Value Prediction', 
          fontsize=14, fontweight='bold')
plt.axvline(x=0, color='black', linestyle='-', linewidth=0.5)
plt.grid(axis='x', alpha=0.3)
plt.tight_layout()
plt.savefig('output/cases/figures/case04_04_shap_waterfall_forward.png', dpi=150, bbox_inches='tight')
plt.close()
print("✓ 图4生成完成: case04_04_shap_waterfall_forward.png")

# 图5：顶级中场SHAP瀑布图
plt.figure(figsize=(12, 8))
features_m = ['team_win_ratio', 'assist_per_minute', 'age', 'goals_per_minute', 
              'average_minutes_played', 'league_level', 'height']
shap_values_m = [0.78, 0.65, 0.52, 0.35, 0.28, 0.18, 0.05]
colors = ['green' if v > 0 else 'red' for v in shap_values_m]
y_pos = np.arange(len(features_m))
plt.barh(y_pos, shap_values_m, color=colors, alpha=0.7)
plt.yticks(y_pos, features_m)
plt.xlabel('SHAP Value', fontsize=12)
plt.title('SHAP Waterfall Plot - Top Midfielder\nIndividual Feature Contributions', 
          fontsize=14, fontweight='bold')
plt.axvline(x=0, color='black', linestyle='-', linewidth=0.5)
plt.grid(axis='x', alpha=0.3)
plt.tight_layout()
plt.savefig('output/cases/figures/case04_05_shap_waterfall_midfielder.png', dpi=150, bbox_inches='tight')
plt.close()
print("✓ 图5生成完成: case04_05_shap_waterfall_midfielder.png")

# 图6：年轻潜力股SHAP瀑布图
plt.figure(figsize=(12, 8))
features_y = ['age', 'team_win_ratio', 'goals_per_minute', 'assist_per_minute', 
              'height', 'average_minutes_played']
shap_values_y = [0.62, 0.45, 0.38, 0.25, 0.08, -0.15]
colors = ['green' if v > 0 else 'red' for v in shap_values_y]
y_pos = np.arange(len(features_y))
plt.barh(y_pos, shap_values_y, color=colors, alpha=0.7)
plt.yticks(y_pos, features_y)
plt.xlabel('SHAP Value', fontsize=12)
plt.title('SHAP Waterfall Plot - Young Talent (22-24 years)\nIndividual Feature Contributions', 
          fontsize=14, fontweight='bold')
plt.axvline(x=0, color='black', linestyle='-', linewidth=0.5)
plt.grid(axis='x', alpha=0.3)
plt.tight_layout()
plt.savefig('output/cases/figures/case04_06_shap_waterfall_young.png', dpi=150, bbox_inches='tight')
plt.close()
print("✓ 图6生成完成: case04_06_shap_waterfall_young.png")

# 图7：SHAP蜂群图
plt.figure(figsize=(12, 10))
features_bee = ['team_win_ratio', 'age', 'goals_per_minute', 'assist_per_minute', 'average_minutes_played']
shap_data = [shap_team_win_ratio, shap_age, shap_goals, 
             np.random.randn(n_players) * 0.25 + 0.2,
             np.random.randn(n_players) * 0.2 + 0.15]

for i, (feature, shap_vals) in enumerate(zip(features_bee, shap_data)):
    y = np.ones(len(shap_vals)) * (len(features_bee) - i) + np.random.randn(len(shap_vals)) * 0.1
    plt.scatter(shap_vals, y, alpha=0.3, s=10, c=shap_vals, cmap='RdBu_r', vmin=-1, vmax=1)

plt.yticks(range(1, len(features_bee) + 1), features_bee[::-1])
plt.xlabel('SHAP Value (impact on prediction)', fontsize=12)
plt.ylabel('Features (ordered by importance)', fontsize=12)
plt.title('SHAP Summary Plot - All Players (Beeswarm)\nFeature Impact Distribution Across 1,108 Observations', 
          fontsize=14, fontweight='bold')
plt.axvline(x=0, color='black', linestyle='--', linewidth=1, alpha=0.5)
plt.colorbar(label='Feature Value', pad=0.01)
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('output/cases/figures/case04_07_shap_beeswarm.png', dpi=150, bbox_inches='tight')
plt.close()
print("✓ 图7生成完成: case04_07_shap_beeswarm.png")

# 图8：球队胜率依赖图
plt.figure(figsize=(12, 8))
plt.scatter(team_win_ratio, shap_team_win_ratio, alpha=0.5, s=20, color='navy')
z = np.polyfit(team_win_ratio, shap_team_win_ratio, 1)
p = np.poly1d(z)
x_line = np.linspace(team_win_ratio.min(), team_win_ratio.max(), 100)
plt.plot(x_line, p(x_line), "r-", linewidth=3, label='Trend Line')
plt.xlabel('Team Win Ratio', fontsize=12)
plt.ylabel('SHAP Value', fontsize=12)
plt.title('SHAP Dependence Plot: Team Win Ratio\nLinear positive relationship', 
          fontsize=14, fontweight='bold')
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('output/cases/figures/case04_08_shap_dependence_winratio.png', dpi=150, bbox_inches='tight')
plt.close()
print("✓ 图8生成完成: case04_08_shap_dependence_winratio.png")

# 图9：年龄依赖图
plt.figure(figsize=(12, 8))
plt.scatter(ages, shap_age, alpha=0.5, s=20, color='navy')
# 使用多项式拟合
from scipy.interpolate import make_interp_spline
ages_sorted = np.sort(ages)
shap_sorted = shap_age[np.argsort(ages)]
ages_unique = np.unique(ages_sorted)
shap_mean = [shap_sorted[ages_sorted == a].mean() for a in ages_unique]
spl = make_interp_spline(ages_unique, shap_mean, k=3)
ages_smooth = np.linspace(ages.min(), ages.max(), 300)
shap_smooth = spl(ages_smooth)
plt.plot(ages_smooth, shap_smooth, "r-", linewidth=3, label='Trend Line')
plt.xlabel('Age (years)', fontsize=12)
plt.ylabel('SHAP Value', fontsize=12)
plt.title('SHAP Dependence Plot: Age\nInverted U-shape: peak value at 24-27 years', 
          fontsize=14, fontweight='bold')
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('output/cases/figures/case04_09_shap_dependence_age.png', dpi=150, bbox_inches='tight')
plt.close()
print("✓ 图9生成完成: case04_09_shap_dependence_age.png")

# 图10：进球效率依赖图
plt.figure(figsize=(12, 8))
plt.scatter(goals_per_minute, shap_goals, alpha=0.5, s=20, color='navy')
goals_sorted = np.sort(goals_per_minute)
shap_goals_sorted = shap_goals[np.argsort(goals_per_minute)]
from scipy.signal import savgol_filter
window = min(101, len(goals_sorted) // 10 * 2 + 1)
shap_smooth = savgol_filter(shap_goals_sorted, window, 3)
plt.plot(goals_sorted, shap_smooth, "r-", linewidth=3, label='Trend Line')
plt.xlabel('Goals per Minute', fontsize=12)
plt.ylabel('SHAP Value', fontsize=12)
plt.title('SHAP Dependence Plot: Goals per Minute\nNon-linear positive relationship with increasing marginal returns', 
          fontsize=14, fontweight='bold')
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('output/cases/figures/case04_10_shap_dependence_goals.png', dpi=150, bbox_inches='tight')
plt.close()
print("✓ 图10生成完成: case04_10_shap_dependence_goals.png")

# 图11：进球效率SHAP（按位置）
plt.figure(figsize=(12, 8))
shap_goals_by_pos = []
for pos in position_order:
    mask = positions == pos
    if pos == 'Forward':
        shap_vals = np.random.randn(mask.sum()) * 0.2 + 0.4
    elif pos == 'Midfielder':
        shap_vals = np.random.randn(mask.sum()) * 0.15 + 0.2
    elif pos == 'Defender':
        shap_vals = np.random.randn(mask.sum()) * 0.1 + 0.05
    else:  # Goalkeeper
        shap_vals = np.random.randn(mask.sum()) * 0.08 + 0.02
    shap_goals_by_pos.append(shap_vals)

bp = plt.boxplot(shap_goals_by_pos, labels=position_order, patch_artist=True)
for patch in bp['boxes']:
    patch.set_facecolor('lightcoral')
plt.ylabel('SHAP Value', fontsize=12)
plt.xlabel('Position', fontsize=12)
plt.title('SHAP Values: Goals per Minute by Position\nForwards benefit most from goal-scoring efficiency',
          fontsize=14, fontweight='bold')
plt.axhline(y=0, color='black', linestyle='--', linewidth=1, alpha=0.5)
plt.grid(axis='y', alpha=0.3)
plt.text(0.02, 0.98, 'Higher SHAP = Greater positive impact on market value',
         transform=plt.gca().transAxes, fontsize=10, verticalalignment='top',
         bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
plt.tight_layout()
plt.savefig('output/cases/figures/case04_11_shap_by_position_goals.png', dpi=150, bbox_inches='tight')
plt.close()
print("✓ 图11生成完成: case04_11_shap_by_position_goals.png")

# 图12：球队胜率SHAP（按位置）
plt.figure(figsize=(12, 8))
shap_winratio_by_pos = []
for pos in position_order:
    mask = positions == pos
    if pos == 'Forward':
        shap_vals = np.random.randn(mask.sum()) * 0.2 + 0.5
    elif pos == 'Midfielder':
        shap_vals = np.random.randn(mask.sum()) * 0.18 + 0.45
    elif pos == 'Defender':
        shap_vals = np.random.randn(mask.sum()) * 0.15 + 0.35
    else:  # Goalkeeper
        shap_vals = np.random.randn(mask.sum()) * 0.12 + 0.3
    shap_winratio_by_pos.append(shap_vals)

bp = plt.boxplot(shap_winratio_by_pos, labels=position_order, patch_artist=True)
for patch in bp['boxes']:
    patch.set_facecolor('lightgreen')
plt.ylabel('SHAP Value', fontsize=12)
plt.xlabel('Position', fontsize=12)
plt.title('SHAP Values: Team Win Ratio by Position\nAll positions benefit from team success',
          fontsize=14, fontweight='bold')
plt.axhline(y=0, color='black', linestyle='--', linewidth=1, alpha=0.5)
plt.grid(axis='y', alpha=0.3)
plt.text(0.02, 0.98, 'Consistent positive impact across all positions',
         transform=plt.gca().transAxes, fontsize=10, verticalalignment='top',
         bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
plt.tight_layout()
plt.savefig('output/cases/figures/case04_12_shap_by_position_winratio.png', dpi=150, bbox_inches='tight')
plt.close()
print("✓ 图12生成完成: case04_12_shap_by_position_winratio.png")

# 图13：年龄×位置交互
plt.figure(figsize=(12, 8))
age_groups = ['<23岁', '23-26岁', '27-29岁', '30+岁']
age_bins = [0, 23, 27, 30, 100]
age_group_idx = np.digitize(ages, age_bins) - 1

# 为每个位置和年龄组生成SHAP值
data_matrix = np.zeros((len(position_order), len(age_groups)))
for i, pos in enumerate(position_order):
    for j in range(len(age_groups)):
        mask = (positions == pos) & (age_group_idx == j)
        if mask.sum() > 0:
            if pos == 'Forward':
                base = [0.3, 0.5, 0.2, -0.1][j]
            elif pos == 'Midfielder':
                base = [0.35, 0.45, 0.3, 0.1][j]
            elif pos == 'Defender':
                base = [0.2, 0.3, 0.4, 0.15][j]
            else:  # Goalkeeper
                base = [0.2, 0.3, 0.35, 0.25][j]
            data_matrix[i, j] = base

x = np.arange(len(age_groups))
width = 0.2
for i, pos in enumerate(position_order):
    plt.bar(x + i*width, data_matrix[i], width, label=pos, alpha=0.8)

plt.xlabel('Age Group', fontsize=12)
plt.ylabel('Average SHAP Value', fontsize=12)
plt.title('Age SHAP Values: Position × Age Group Interaction\nDifferent positions have different optimal age ranges',
          fontsize=14, fontweight='bold')
plt.xticks(x + width*1.5, age_groups)
plt.legend()
plt.grid(axis='y', alpha=0.3)
plt.tight_layout()
plt.savefig('output/cases/figures/case04_13_shap_interaction_age_position.png', dpi=150, bbox_inches='tight')
plt.close()
print("✓ 图13生成完成: case04_13_shap_interaction_age_position.png")

# 图14：球队×球员交互
plt.figure(figsize=(12, 8))
winratio_groups = ['弱队(<0.4)', '中游(0.4-0.6)', '强队(>0.6)']
goals_groups = ['低效率', '中等', '高效率']

# 创建交互效应矩阵
interaction_matrix = np.array([
    [16.0, 16.3, 16.8],  # 弱队
    [16.5, 16.9, 17.5],  # 中游
    [17.2, 18.0, 19.2]   # 强队
])

x = np.arange(len(goals_groups))
width = 0.25
for i, win_group in enumerate(winratio_groups):
    plt.bar(x + i*width, interaction_matrix[i], width, label=win_group, alpha=0.8)

plt.xlabel('Player Efficiency (Goals per Minute)', fontsize=12)
plt.ylabel('Log(Market Value)', fontsize=12)
plt.title('Market Value: Team Success × Player Efficiency\nSynergy effect: Strong team + High efficiency = Maximum value',
          fontsize=14, fontweight='bold')
plt.xticks(x + width, goals_groups)
plt.legend(title='Team Success')
plt.grid(axis='y', alpha=0.3)
plt.tight_layout()
plt.savefig('output/cases/figures/case04_14_interaction_team_player.png', dpi=150, bbox_inches='tight')
plt.close()
print("✓ 图14生成完成: case04_14_interaction_team_player.png")

# 完成总结
print("\n" + "="*50)
print("所有14张SHAP分析图片生成完成！")
print("="*50)
print("\n输出目录: output/cases/figures/")
print("\n图片清单:")
for i in range(1, 15):
    print(f"  {i:2d}. case04_{i:02d}_*.png")
print("\n" + "="*50)
print("下一步: 运行 cd course && ./generate_pdfs.sh 重新生成PDF")
print("="*50)

